#pragma once

#include <string>
#include "BitMap.h"
#include "IllegalArgumentException.h"
#include "FileException.h"
#include "Decompressor.h"

using namespace std;
template <class pixelType>
class MyImage
{
private:
	string _fileName;
	BitMap<pixelType> *_bitmap;
protected:
	void bitMapLoader(std::string fileName)throw(FileException, IllegalArgumentException){
		FILE *fp;
		errno_t err = fopen_s(&fp,fileName.c_str(),"rb");
		if(err!=0){
			throw FileException("MyImage Class, File not exist"); 
		}

		fseek(fp,0L,SEEK_END);

		unsigned int fSize=ftell(fp);
		rewind(fp);
		if(fSize < sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)){
			throw FileException("MyImage Class, Incorrect file type"); 
		}

		BITMAPFILEHEADER bmpHeader;
		BITMAPINFOHEADER bmpInfo;
		vector <RGBQUAD> rgbQuad;
		vector<pixelType> imageData;

		int ret = fread(&bmpHeader,sizeof(bmpHeader),1,fp);
		if(bmpHeader.bfType != 0x4D42) {
			fclose(fp);
			throw FileException("MyImage Class,Incorrect bmp file type"); 
		}
		if(bmpHeader.bfOffBits == 0) { 
			fclose(fp);
			throw FileException("MyImage Class,Incorrect bmp file header"); 
		}

		ret=fread(&bmpInfo,sizeof(bmpInfo),1,fp);

		if(bmpInfo.biClrUsed!=0){
			rgbQuad.resize(bmpInfo.biClrUsed);
			ret=fread(&rgbQuad.at(0),bmpInfo.biClrUsed*sizeof(RGBQUAD),1,fp);
		}

		fseek(fp, bmpHeader.bfOffBits, SEEK_SET);
		imageData.resize(bmpInfo.biSizeImage,0);
		fread(&imageData.at(0),imageData.size()*sizeof(unsigned char),1,fp);

		unsigned int padding = 3*bmpInfo.biWidth%4;
		// if bmpInfo.biCompression == BI_RGB then no need decompression
		
		Decompressor dec(imageData,padding);
		if (bmpInfo.biCompression == BI_RLE8){
			imageData = dec.Rle8();
			bmpInfo.biCompression=BI_RGB;
			bmpInfo.biSizeImage = imageData.size();
		}else
		if (bmpInfo.biCompression == BI_RLE4){
			//load rle4 decompression
		}else
		if (bmpInfo.biCompression == BI_BITFIELDS){
			// load uncompressed rgb. This is valid when used with 16- and 32-bpp bitmaps.
		}else
		if (bmpInfo.biCompression == BI_JPEG){
			//load as jpeg decompression
		}else
		if (bmpInfo.biCompression == BI_PNG){
			//load as png decompression
		}
		fclose(fp);
		try{
			_bitmap = new BitMap<pixelType>(bmpHeader,bmpInfo,rgbQuad,imageData);
		}catch(IllegalArgumentException e){
			throw e;
		}
		
	};

public:
	MyImage(string fileName) throw(IllegalArgumentException,FileException){
		if(fileName.empty()){
			throw IllegalArgumentException("MyImage Class, file name is empty");
		}

		if(fileName==""){
			throw IllegalArgumentException("MyImage Class, file name is empty");
		}

		string fileType;
		int i = fileName.size()-1;

		while (fileName.at(i)!='.'){
			if (i<=0){
				throw IllegalArgumentException("MyImage Class, unknown file type");
			}
			i--;
		}

		fileType = fileName.substr(i+1,fileName.size()-i);
		_fileName = fileName;

		if(fileType=="bmp"){
			bitMapLoader(_fileName);
		}
	};


	MyImage(BitMap<pixelType> bitmap){
			_bitmap = new BitMap<pixelType>(bitmap.getBitmapFileHeader(),bitmap.getBitmapInfoHeader(),bitmap.getPalette(),bitmap.getImageData());
	};


	BitMap<pixelType> getImageBitMapSample(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2){
		return _bitmap->getBimapArea(x1,y1,x2,y2);
	};


	void getImageResolution(unsigned int *x, unsigned int *y){
		*x = _bitmap->getBitmapInfoHeader().biWidth;
		*y = _bitmap->getBitmapInfoHeader().biHeight;
	};


	void saveImage(std::string fileName, std::string type){
		FILE *fp;
		string fName = fileName+"."+type;
		errno_t err = fopen_s(&fp,fName.c_str(),"wb");

		fwrite(&_bitmap->getBitmapFileHeader(),sizeof(BITMAPFILEHEADER),1,fp);
		fwrite(&_bitmap->getBitmapInfoHeader(),sizeof(BITMAPINFOHEADER),1,fp);

		int clUsed = _bitmap->getBitmapInfoHeader().biClrUsed;
		if(clUsed!=0){
			fwrite(&_bitmap->getPalette().at(0),_bitmap->getPalette().size()*sizeof(RGBQUAD),1,fp);
		}

		fwrite(&_bitmap->getImageData().at(0),_bitmap->getImageData().size()*sizeof(unsigned char),1,fp);

		fclose(fp);
	};


	vector<float> extractImageData(){
		vector<pixelType> charData = _bitmap->getImageData();
		vector<float> floatData;
		floatData.resize(charData.size());
		for (unsigned int i=0; i< charData.size(); i++){
			floatData[i] = static_cast<float>(charData[i]);
		}
		return floatData;
	};
	
	
	~MyImage(void){
		delete _bitmap;
	};
};

